package com.simple.trade.service.vm.impl;

import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
import com.simple.common.entity.ReqContext;
import com.simple.common.entity.ResData;
import com.simple.common.entity.constant.Payment;
import com.simple.common.entity.dto.pay.vm.RefundTradeDto;
import com.simple.common.entity.dto.pay.vm.RefundTradeRspDto;
import com.simple.common.entity.po.sys.*;
import com.simple.common.entity.req.vm.pay.RefundTradeReq;
import com.simple.common.entity.vo.pay.RefundTradeRes;
import com.simple.common.framework.scale.Scale;
import com.simple.ds.service.RefundTradeApi;
import com.simple.ds.service.RefundTradeInventoryApi;
import com.simple.trade.holder.PayContext;
import com.simple.trade.service.vm.RefundTradeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.Objects;

/**
 * @author yasud
 * @date 2020年10月10日 14:34
 * @description
 */
@Slf4j
@Service
public class RefundTradeServiceImpl extends AbstractService implements RefundTradeService {

    @Autowired
    RefundTradeApi refundTradeApi;

    @Autowired
    RefundTradeInventoryApi refundTradeInventoryApi;


    /**
     * 退款
     *
     * @param req
     * @return
     */
    @Override
    public ResData<RefundTradeRes> refundTrade(RefundTradeReq req) {
        log.info("商户订单{}申请退款", req.getOrderNo());
        Trade trade = tradeApi.findByPropertyOne_(Maps.newHashMap(ImmutableMap.of(
                "merchant_order_no", req.getOrderNo(),
                "app_id", req.getAppId()
        )));
        Preconditions.checkNotNull(trade, "订单不存在");
        String refundAmount = req.getRefundAmount();
        if (!trade.getCurrency().equals(trade.getPayAmountCurrency())) {
            BigDecimal v1 = new BigDecimal(trade.getAmount());
            BigDecimal v2 = Scale.scale(req.getRefundAmount());
            Preconditions.checkState(v1.compareTo(v2) == 0, "不支持部分退款");
            refundAmount = trade.getPayAmount();
        }
        PayContext.current().iam(trade);
        Pair<ChannelComp, Product> pair = findChannelAnProduct();
        ChannelComp channel = pair.getLeft();
        Product product = pair.getRight();
        //先保存
        RefundTrade refundTrade = refundTradeApi.findByTradeId(trade.getId());
        if (Objects.isNull(refundTrade)) {
            refundTrade = refundTradeApi.save_(generateRefundTrade(trade, req, channel));
        }
        log.info("商户订单{}对应退款订单为{}", req.getOrderNo(), refundTrade.getId());
        Preconditions.checkState(isSufficientRefundAmount(trade, new BigDecimal(req.getRefundAmount())), "退款金额不足");
        RefundTradeInventory refundTradeInventory = refundTradeInventoryApi.save_(generateRefundTradeInventory(refundTrade, req, refundAmount, "商户发起"));
        log.info("商户订单{}的退款订单{}创建流水{}", req.getOrderNo(), refundTrade.getId(), refundTradeInventory);
        RefundTradeDto refundTradeDto = RefundTradeDto.generateDto(trade, refundTrade, refundTradeInventory, PayContext.current().getContext(), req.getExtra(), channel, product);
        ResData<RefundTradeRspDto> refundTradeRspDtoResData = forwardToChannel(refundTradeDto, channel, Payment.PAY_TYPE.VM_REFUND_TRADE, RefundTradeRspDto.class);
        if (refundTradeRspDtoResData.isOk()) {
            RefundTradeRspDto rspDto = refundTradeRspDtoResData.getData();

            RefundTradeInventory inventory = rspDto.getInventory();
            Preconditions.checkNotNull(inventory, "返回退款交易不能为空!!");
            RefundTrade rspDtoRefundTrade = rspDto.getRefundTrade();
            Preconditions.checkNotNull(rspDtoRefundTrade, "返回退款订单不能为空!!");
            Trade rspDtoTrade = rspDto.getTrade();
            Preconditions.checkNotNull(rspDtoTrade, "返回交易不能为空!!");

            //只有确定退款完成之后才能做操作
            if (inventory.getStatus() == RefundTradeInventory.Status.SUCCESS) {
                log.info("商户订单{}退款成功, 返回结果{}", req.getOrderNo(), refundTradeRspDtoResData);
                inventory.setRefundTime(new Date());
                rspDtoRefundTrade.addRefundAmount(inventory.getRefundAmount());
                rspDtoRefundTrade.addRealRefundAmount(inventory.getRealRefundAmount());
                rspDtoTrade.addRefundAmount(req.getRefundAmount());
                rspDtoTrade.setRefundStatus(getRefundedStatus(trade, new BigDecimal(req.getRefundAmount())));
            } else if (inventory.getStatus() == RefundTradeInventory.Status.PENDING || inventory.getStatus() == RefundTradeInventory.Status.ACCEPT) {
                log.info("商户订单{}退款处理中, 返回结果{}", req.getOrderNo(), refundTradeRspDtoResData);
            } else {
                log.info("商户订单{}退款失败, 返回结果{}", req.getOrderNo(), refundTradeRspDtoResData);
            }
            tradeApi.updateById_(rspDtoTrade);
            refundTradeApi.updateById_(rspDtoRefundTrade);
            refundTradeInventoryApi.updateById_(inventory);
            return ResData.ok(generateRefundTradeRes(rspDtoRefundTrade, inventory, rspDtoTrade));
        } else {
            log.info("商户订单{}退款失败，原因{}", req.getOrderNo(), refundTradeRspDtoResData.getMsg());
            refundTradeInventory.setChannelRefundMsg(refundTradeRspDtoResData.getMsg());
            refundTradeInventory.setStatus(RefundTradeInventory.Status.FAILED);
            refundTradeInventoryApi.updateById_(refundTradeInventory);
            return ResData.error(refundTradeRspDtoResData.getMsg());
        }
    }

    /**
     * 构建退款返回
     *
     * @param refundTrade
     * @param inventory
     * @param trade
     * @return
     */
    private RefundTradeRes generateRefundTradeRes(RefundTrade refundTrade,
                                                  RefundTradeInventory inventory,
                                                  Trade trade) {
        RefundTradeRes refundTradeRes = new RefundTradeRes();
        refundTradeRes.setCurrency(refundTrade.getCurrency());
        refundTradeRes.setRefundAmount(inventory.getRefundAmount());
        refundTradeRes.setSerialNo(inventory.getId());
        refundTradeRes.setOrderNo(trade.getMerchantOrderNo());
        refundTradeRes.setRefundStatus(inventory.getStatus());
        refundTradeRes.setRefundAmount(inventory.getRefundAmount());
        return refundTradeRes;
    }

    private RefundTrade generateRefundTrade(Trade trade, RefundTradeReq req, ChannelComp channel) {
        ReqContext context = PayContext.current().getContext();
        Merchant merchant = context.getMerchant();
        Application app = context.getApp();
        AppExample example = context.getExample();
        RefundTrade refundTrade = new RefundTrade();
        refundTrade.setMerchantNo(merchant.getMerchantNo());
        refundTrade.setMerchantName(merchant.getName());
        refundTrade.setApplicationId(app.getId());
        refundTrade.setAppId(example.getAppId());
        refundTrade.setAppName(example.getName());
        refundTrade.setChannelCode(channel.getCode());
        refundTrade.setChannelName(channel.getName());
        refundTrade.setTradeId(trade.getId());
        refundTrade.setTradeAmount(trade.getAmount());
        refundTrade.setCurrency(trade.getCurrency());
        refundTrade.setRealRefundCurrency(trade.getPayAmountCurrency());
        refundTrade.setRefundAmount("0");
        refundTrade.setRealRefundTotalAmount("0");
        return refundTrade;
    }

    private RefundTradeInventory generateRefundTradeInventory(RefundTrade refundTrade, RefundTradeReq req, String realRefundAmount, String refundOriginator) {
        RefundTradeInventory refundTradeInventory = new RefundTradeInventory();
        refundTradeInventory.setRefundTradeId(refundTrade.getId());
        refundTradeInventory.setRefundAmount(req.getRefundAmount());
        refundTradeInventory.setRealRefundAmount(realRefundAmount);
        refundTradeInventory.setExtra(req.getExtra());
        refundTradeInventory.setRefundReason(req.getRefundReason());
        refundTradeInventory.setStatus(RefundTradeInventory.Status.ACCEPT);
        refundTradeInventory.setAuditStatus(RefundTradeInventory.AuditStatus.NONE);
        refundTradeInventory.setRefundTime(new Date());
        refundTradeInventory.setRefundOriginator(refundOriginator);
        return refundTradeInventory;
    }

    /**
     * 是否有足够的退款金额
     *
     * @param trade
     * @param refundAmount
     * @return
     */
    private boolean isSufficientRefundAmount(Trade trade, BigDecimal refundAmount) {
        return Scale.scale(new BigDecimal(trade.getAmount()))
                .subtract(Scale.scale(new BigDecimal(trade.getRefundAmount()))).compareTo(refundAmount) >= 0;
    }

    /**
     * 获取退款状态
     *
     * @param trade
     * @param refundAmount
     * @return
     */
    private Trade.RefundStatus getRefundedStatus(Trade trade, BigDecimal refundAmount) {
        BigDecimal v1 = new BigDecimal(trade.getAmount()),
                v2 = new BigDecimal(trade.getRefundAmount());
        if (Scale.scale(v1.subtract(v2)).compareTo(Scale.scale(refundAmount)) == 0) {
            return Trade.RefundStatus.REFUND_FULL;
        } else if (Scale.scale(v1.subtract(v2)).compareTo(Scale.scale(refundAmount)) >= 1) {
            return Trade.RefundStatus.REFUND_PART;
        } else {
            Preconditions.checkState(false, "退款无效");
            return null;
        }
    }
}
